奄奄一息的山姆躺在地上,腦海中浮現了人生跑馬燈。
「我為什麼會在這裡?我的夢想,終究只是夢想吧...。」
『要嘛等死,要嘛寫歌』出自《反正我很閒》的一段話冒了出來。
「對!這是發生山難的生存法則,我怎麼給忘了呢!」山姆跳了起來。
「寫歌嗎?找我就對了」一個名為伊索雷托的森林小精靈望著山姆,手裡抱著一把三味線。
PS. 這裡是開發 iOS 手機遊戲的系列文,如果還沒看過之前
劇情文章的朋友,歡迎先點這邊回顧唷!
我想一個遊戲的靈魂,最重要的就在於背景音樂跟音效了。雖然老姐我可以自己畫圖跟寫程式碼,但是音樂創作還是很講求天份的!這個就交給專業的老弟來吧!
風格:8-bit 復古遊戲風格的音樂
音樂:
音效:
請直接將所有 mp3 檔案拖移到專案左側的檔案導覽器中,會跳出以下的訊息,請點擊 Finish

請先在遊戲場景中 import AVFoundation,它是一個框架,可以用來處理視聽媒體
import AVFoundation
在遊戲場景中新增 4 個播放器,可以將它想像成四個音軌,分別播放背景音樂、主角與收集物碰觸的音效、主角與怪物碰觸的音效、點擊方向鍵的音效,彼此間可以互相疊加。
分別新增四個播放器播音樂的方法,讓呼叫方法的時候可以帶入檔名 musicName ,其中音樂類型的再加上是否循環播放的參數 loop,並且設定預設為 false
方法內容:
Bundle.main.url 取得指定的檔案位置,並新增一個播放器來播放音樂檔,使用 play() 可以開始播放音樂numberOfLoops 可以設定重複播放的次數,預設為 0,音樂只會播一次,不會重複。設定 -1 則會不斷循環播放,直到呼叫 stop() 才會停止class GameScene: SKScene {
    ...
    var musicPlayer: AVAudioPlayer?
    var soundCollectionPlayer: AVAudioPlayer?
    var soundWeatherPlayer: AVAudioPlayer?
    var soundClickPlayer: AVAudioPlayer?
    
    func playMusicByName(musicName: String, loop: Bool = false) {
        guard let url = Bundle.main.url(forResource: musicName, withExtension: "mp3") else {
            return
        }
        do {
            self.musicPlayer = try AVAudioPlayer(contentsOf: url)
            self.musicPlayer!.play()
            if loop {
                self.musicPlayer?.numberOfLoops = -1
            }
        }
        catch {
            print(error)
        }
    }
    
    func playCollectionSoundByName(soundName: String) {
        guard let url = Bundle.main.url(forResource: soundName, withExtension: "mp3") else {
            return
        }
        do {
            self.soundCollectionPlayer = try AVAudioPlayer(contentsOf: url)
            self.soundCollectionPlayer!.play()
        }
        catch {
            print(error)
        }
    }
    
    func playWeatherSoundByName(soundName: String) {
        guard let url = Bundle.main.url(forResource: soundName, withExtension: "mp3") else {
            return
        }
        do {
            self.soundWeatherPlayer = try AVAudioPlayer(contentsOf: url)
            self.soundWeatherPlayer!.play()
        }
        catch {
            print(error)
        }
    }
    
    func playClickSound() {
        guard let url = Bundle.main.url(forResource: "ClickSound", withExtension: "mp3") else {
            return
        }
        do {
            self.soundClickPlayer = try AVAudioPlayer(contentsOf: url)
            self.soundClickPlayer!.play()
        }
        catch {
            print(error)
        }
    }
}
StartMusic
playMusicByName 方法,musicName 帶入音樂檔名false
class GameScene: SKScene {
    ...
    func gameStart() {
        ...
        self.playMusicByName(musicName: "StartMusic")
    }
}
BaseMusic
playMusicByName 方法,musicName 帶入音樂檔名loop 帶入 true
playMusicByName 方法播放背景音樂1class GameScene: SKScene {
    ...
    @objc func gameStartAction() {
        ...
        self.playMusicByName(musicName: "BaseMusic", loop: true)
    }
}
class GameScene: SKScene {
    ...
    // 逃跑->攻擊
    @objc func eacapeToAttackModeAction() {
        ...
        self.playMusicByName(musicName: "BaseMusic", loop: true)
    }
}
FastMusic
playMusicByName 方法,musicName 帶入音樂檔名loop 帶入 true
class GameScene: SKScene {
    ...
    override func update(_ currentTime: TimeInterval) {
        ...
        for magicCrystal in self.magicCrystals where !magicCrystal.isGotten && magicCrystal.gridX == sam.gridX && magicCrystal.gridY == sam.gridY {
            ...
            self.playMusicByName(musicName: "FastMusic", loop: true)
        }
    }
}
GameOverMusic
playMusicGameOver 方法,找到音樂檔案,並且播放playMusicGameOver 方法self.musicGameOver?.stop()
import AVFoundation
class GameOverScene: SKScene {
    ...
    var musicGameOver: AVAudioPlayer?
    override func didMove(to view: SKView) {
        ...
        self.playMusicGameOver()
    }
    func playMusicGameOver() {
        if let url = Bundle.main.url(forResource: "GameOverMusic", withExtension: "mp3") {
            self.musicGameOver = try? AVAudioPlayer(contentsOf: url)
            self.musicGameOver?.play()
        }
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in (touches) {
            let location = touch.location(in: self)
            if self.atPoint(location) == self.restartNode || self.atPoint(location) == self.labelRestart {
                if let gameScene = GameScene(fileNamed: "GameScene") {
                    ...
                    self.musicGameOver?.stop()
                }
            }
        }
    }
}
FallSound
stop() 將背景音樂關閉playWeatherSoundByName 方法,soundName 帶入音效檔名class GameScene: SKScene {
    ...
    func gameStop() {
        ...
        self.musicPlayer?.stop()
        self.playWeatherSoundByName(soundName: "FallSound")
    }
}
HitSound
playWeatherSoundByName 方法,soundName 帶入音效檔名class GameScene: SKScene {
    ...
    override func update(_ currentTime: TimeInterval) {
        ...
        for weather in self.weathers where (weather.gridX == sam.gridX && abs(weather.node.position.y - sam.node.position.y) <= CGFloat(self.gridWH + 6) || weather.gridY == sam.gridY && abs(weather.node.position.x - sam.node.position.x) <= CGFloat(self.gridWH + 6)) && (gridMapping.purpleTree.x != sam.gridX && gridMapping.purpleTree.y != sam.gridY)
        {
            if weather.mode == .ATTACK || weather.mode == .PLAY {
                ...
            } else if weather.mode == .ESCAPE {
                ...
                self.playWeatherSoundByName(soundName: "HitSound")
            }
        }
    }
}
GotSound
playCollectionSoundByName 方法,soundName 帶入音效檔名class GameScene: SKScene {
    ...
    override func update(_ currentTime: TimeInterval) {
        ...
        for crystal in self.crystals where !crystal.isGotten && crystal.gridX == sam.gridX && crystal.gridY == sam.gridY {
            ...
            self.playCollectionSoundByName(soundName: "GotSound")
        }
        for magicCrystal in self.magicCrystals where !magicCrystal.isGotten && magicCrystal.gridX == sam.gridX && magicCrystal.gridY == sam.gridY {
            ...
            self.playCollectionSoundByName(soundName: "GotSound")
        }
    }
}
EatSound
playCollectionSoundByName 方法,soundName 帶入音效檔名class GameScene: SKScene {
    ...
    override func update(_ currentTime: TimeInterval) {
        ...
        for mushroom in self.mushrooms where !mushroom.isGotten && mushroom.gridX == sam.gridX && mushroom.gridY == sam.gridY {
            ...
            self.playCollectionSoundByName(soundName: "EatSound")
        }
    }
}
ClickSound
playClickSound 方法class GameScene: SKScene {
    ...
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        ...
        for touch in (touches) {
            let location = touch.location(in: self)
            if self.atPoint(location) == btnLeft {
                ...
                self.playClickSound()
            }
            if self.atPoint(location) == btnRight {
                ...
                self.playClickSound()
            }
            if self.atPoint(location) == btnUp {
                ...
                self.playClickSound()
            }
            if self.atPoint(location) == btnDown {
                ...
                self.playClickSound()
            }
        }
    }
}
目前遊戲已經套用音樂及音效了,還差一個遊戲破關音樂還沒套用。
明日會帶大家實作遊戲破關時的動作,到時候再將音樂也套用上去吧!